package com.zndroid.base.ui.impl;

import android.content.Context;
import android.os.Bundle;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewbinding.ViewBinding;

import com.zndroid.base.callback.inner.IOnFragmentVisibilityChangedListener;
import com.zndroid.base.callback.inner.mvp.IView;

import java.util.ArrayList;

/**
 * Fragment 可见性监听方案 - 完美兼容多种 case
 * @author lzy
 * @date 2021/3/25
 *
 * 支持以下四种 case
 * 1. 支持 viewPager 嵌套 fragment，主要是通过 setUserVisibleHint 兼容，
 * FragmentStatePagerAdapter BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 的 case，因为这时候不会调用 setUserVisibleHint 方法，在 onResume check 可以兼容
 * 2. 直接 fragment 直接 add， hide 主要是通过 onHiddenChanged
 * 3. 直接 fragment 直接 replace ，主要是在 onResume 做判断
 * 4. Fragment 里面用 ViewPager， ViewPager 里面有多个 Fragment 的，通过 setOnVisibilityChangedListener 兼容，
 * 前提是一级 Fragment 和 二级 Fragment 都必须继承  BaseVisibilityFragment, 且必须用 FragmentPagerAdapter 或者 FragmentStatePagerAdapter
 * 项目当中一级 ViewPager adapter 比较特殊，不是 FragmentPagerAdapter，也不是 FragmentStatePagerAdapter，导致这种方式用不了
 */
@SuppressWarnings("unused")
public abstract class BaseVisibilityFragment<VB extends ViewBinding, P extends BasePresenter<? extends IView>> extends BaseFragment<VB, P> implements
        View.OnAttachStateChangeListener,
        IOnFragmentVisibilityChangedListener
{

    /**
     * ParentActivity是否可见
     */
    private boolean parentActivityVisible = false;

    /**
     * 是否可见（Activity处于前台、Tab被选中、Fragment被添加、Fragment没有隐藏、Fragment.View已经Attach）
     */
    private boolean visible = false;

    private BaseVisibilityFragment<VB, P> localParentFragment = null;
    private final ArrayList<IOnFragmentVisibilityChangedListener> listeners = new ArrayList<>();

    private void addOnVisibilityChangedListener(IOnFragmentVisibilityChangedListener listener) {
        this.listeners.add(listener);
    }

    private void removeOnVisibilityChangedListener(IOnFragmentVisibilityChangedListener listener) {
        this.listeners.remove(listener);
    }

    /**
     * 检查可见性是否变化
     *
     * @param expected 可见性期望的值。只有当前值和expected不同，才需要做判断
     */
    private void checkVisibility(boolean expected) {
        if (expected == visible) {
            onBackVisibilityChanged(expected);
            return;
        }
        boolean parentVisible;
        if (null == localParentFragment) {
            parentVisible = parentActivityVisible;
        } else {
            parentVisible = localParentFragment.isVisible();
        }
        boolean superVisible = super.isVisible();
        boolean hintVisible = getUserVisibleHint();
        boolean visible = parentVisible && superVisible && hintVisible;

        if (visible != this.visible) {
            this.visible = visible;
            dispatchChild(this.visible);
        }

        onBackVisibilityChanged(this.visible);
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        checkVisibility(hidden);
    }

    /**
     * Tab切换时会回调此方法。对于没有Tab的页面，[Fragment.getUserVisibleHint]默认为true
     * */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        checkVisibility(isVisibleToUser);
    }

    @Override
    public void onViewAttachedToWindow(View v) {
        checkVisibility(true);
    }

    @Override
    public void onViewDetachedFromWindow(View v) {
        v.removeOnAttachStateChangeListener(this);
        checkVisibility(false);
    }

    @Override
    public void onFragmentVisibilityChanged(boolean visible) {
        checkVisibility(visible);
    }

    /**
     * ParentActivity可见性改变
     * @param visible visible
     */
    protected void onActivityVisibilityChanged(boolean visible) {
        parentActivityVisible = visible;
        checkVisibility(visible);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        // 处理直接 replace 的 case
        view.addOnAttachStateChangeListener(this);
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        Fragment parentFragment = getParentFragment();
        if ((parentFragment instanceof BaseVisibilityFragment)) {
            this.localParentFragment = (BaseVisibilityFragment<VB, P>) parentFragment;
            localParentFragment.addOnVisibilityChangedListener(this);
        }
        checkVisibility(true);
    }

    @Override
    public void onDetach() {
        if (null != localParentFragment) {
            localParentFragment.removeOnVisibilityChangedListener(this);
        }
        super.onDetach();
        checkVisibility(false);
        if (null != localParentFragment) {
            localParentFragment = null;
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        onActivityVisibilityChanged(true);
    }

    @Override
    public void onPause() {
        super.onPause();
        onActivityVisibilityChanged(false);
    }

    private void dispatchChild(boolean visible) {
        for (IOnFragmentVisibilityChangedListener it : listeners) {
            it.onFragmentVisibilityChanged(visible);
        }
    }

    /**
     * call back of visibility
     * @param visible true or false
     * */
    protected void onBackVisibilityChanged(boolean visible) {

    }

    /**
     * 是否可见（Activity处于前台、Tab被选中、Fragment被添加、Fragment没有隐藏、Fragment.View已经Attach）
     */
    public boolean isFragmentVisible() {
       return visible;
    }
}
